home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / AppsToGo / Kibitz / Notation.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-24  |  16.3 KB  |  694 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        notation.c
  5. ** Written by:  Eric Soldan
  6. **
  7. ** Copyright © 1990-1992 Apple Computer, Inc.
  8. ** All rights reserved. */
  9.  
  10.  
  11.  
  12. /*****************************************************************************/
  13.  
  14.  
  15.  
  16. #include "Kibitz.h"                /* Get the Kibitz includes/typedefs, etc.    */
  17. #include "KibitzCommon.h"        /* Get the stuff in common with rez.        */
  18. #include "Kibitz.protos"        /* Get the prototypes for Kibitz.            */
  19.  
  20. #ifndef __MEMORY__
  21. #include <Memory.h>
  22. #endif
  23.  
  24. #ifndef __RESOURCES__
  25. #include <Resources.h>
  26. #endif
  27.  
  28. #ifndef __TEXTEDITCONTROL__
  29. #include <TextEditControl.h>
  30. #endif
  31.  
  32. #ifndef __TOOLUTILS__
  33. #include <ToolUtils.h>
  34. #endif
  35.  
  36. #ifndef __UTILITIES__
  37. #include <Utilities.h>
  38. #endif
  39.  
  40.  
  41.  
  42. /*****************************************************************************/
  43.  
  44.  
  45.  
  46. #pragma segment Window
  47. Boolean    Algebraic(FileRecHndl frHndl, short doMoveNum, short gameIndex, StringPtr pstr)
  48. {
  49.     short            from, to, piece, extend, rowMatch, colMatch;
  50.     short            i, r, c, rr, cc, rrr, ccc, fff, ttt, ppp;
  51.     short            color, kingLoc;
  52.     GameListHndl    gameMoves;
  53.     MoveListHndl    legalMoves;
  54.  
  55.     gameMoves = (*frHndl)->doc.gameMoves;
  56.     from = (**gameMoves)[doMoveNum].moveFrom;
  57.     to   = (**gameMoves)[doMoveNum].moveTo;
  58.  
  59.     if (!from) {
  60.         GetIndString(pstr, rGameStat, to);
  61.         while (pstr[1] == ' ') BlockMove(pstr + 2, pstr + 1, --pstr[0]);
  62.         return(false);
  63.     }
  64.  
  65.     r  = from / 10;
  66.     c  = from - 10 * r - 1;
  67.     r -= 2;
  68.  
  69.     rr  = to / 10;
  70.     cc  = to - 10 * rr - 1;
  71.     rr -= 2;
  72.  
  73.     piece = (*frHndl)->doc.theBoard[from];
  74.     if (piece < 0) piece = -piece;
  75.  
  76.     pstr[0] = 0;
  77.     if (piece == PAWN) {
  78.         if (c != cc) {
  79.             pstr[++pstr[0]] = "abcdefgh"[c];
  80.             pstr[++pstr[0]] = 'x';
  81.         }
  82.     }
  83.     if (piece == KING) {
  84.         i = c - cc;
  85.         if (i == 2) {
  86.             pcpy(pstr, "\pO-O-O");
  87.             piece = EMPTY;
  88.         }
  89.         if (i == -2) {
  90.             pcpy(pstr, "\pO-O");
  91.             piece = EMPTY;
  92.         }
  93.     }
  94.  
  95.     if (piece > PAWN) {
  96.         rowMatch = colMatch = extend = false;
  97.         GenerateLegalMoves(frHndl);
  98.         legalMoves = (*frHndl)->doc.legalMoves;
  99.         for (i = 0; i < (*frHndl)->doc.numLegalMoves; ++i) {
  100.             fff = (**legalMoves)[i].moveFrom;
  101.             ttt = (**legalMoves)[i].moveTo;
  102.             if ((ttt == to) && (fff != from)) {
  103.                 ppp = (*frHndl)->doc.theBoard[fff];
  104.                 if (ppp < 0) ppp = -ppp;
  105.                 if (ppp != piece) continue;
  106.                 rrr  = fff / 10;
  107.                 ccc  = fff - 10 * rrr - 1;
  108.                 rrr -= 2;
  109.                 extend = true;
  110.                 if (r == rrr) rowMatch = true;
  111.                 if (c == ccc) colMatch = true;
  112.             }
  113.         }
  114.         if ((extend) && (!colMatch)) rowMatch |= extend;
  115.         pstr[++pstr[0]] = "  NBRQK"[piece];
  116.         if (rowMatch)
  117.             pstr[++pstr[0]] = "abcdefgh"[c];
  118.         if (colMatch)
  119.             pstr[++pstr[0]] = "87654321"[r];
  120.         i = (*frHndl)->doc.theBoard[to];
  121.         if ((i) && (i != OBNDS)) pstr[++pstr[0]] = 'x';
  122.  
  123.     }
  124.     if (piece) {
  125.         pstr[++pstr[0]] = "abcdefgh"[cc];
  126.         pstr[++pstr[0]] = "87654321"[rr];
  127.     }
  128.     if (piece == PAWN) {
  129.         to = (**gameMoves)[doMoveNum].promoteTo;
  130.         if (to) {
  131.             if (to < 0) to = -to;
  132.             pstr[++pstr[0]] = '=';
  133.             pstr[++pstr[0]] = "  NBRQ"[to];
  134.         }
  135.     }
  136.  
  137.     if (doMoveNum < (*frHndl)->doc.numGameMoves) {
  138.         RepositionBoard(frHndl, doMoveNum + 1, false);
  139.         kingLoc = (*frHndl)->doc.king[color = WhosMove(frHndl)].kingLoc;
  140.         if (SquareAttacked(frHndl, kingLoc, color)) pstr[++pstr[0]] = '+';
  141.         RepositionBoard(frHndl, doMoveNum, false);
  142.     }
  143.  
  144.     return(doMoveNum == gameIndex - 1);
  145. }
  146.  
  147.  
  148.  
  149. /*****************************************************************************/
  150.  
  151.  
  152.  
  153. #pragma segment Window
  154. void    MovesToOutBox(FileRecHndl frHndl, EventRecord *event)
  155. {
  156.     short        txtIndx, numGameMoves, gameIndex, startColor;
  157.     short        move, moveColor, moveNum, i, selStart, selEnd, condensed;
  158.     Boolean        needCR;
  159.     Str255        txt;
  160.     Handle        txtHndl;
  161.     TEHandle    teHndl;
  162.     WindowPtr    oldPort;
  163.  
  164.     txtHndl = NewHandle(32000);
  165.     if (!txtHndl) return;
  166.     txtIndx = 0;
  167.  
  168.     condensed = (event->modifiers & optionKey);
  169.  
  170.     numGameMoves = (*frHndl)->doc.numGameMoves;
  171.     gameIndex    = (*frHndl)->doc.gameIndex;
  172.     teHndl       = (*frHndl)->doc.message[kMessageOut];
  173.     startColor   = (*frHndl)->doc.startColor;
  174.  
  175.     txt[0] = 0;
  176.     needCR = false;
  177.     for (move = gameIndex; move < numGameMoves; ++move) {
  178.         moveColor = ((move + startColor) & 0x01);
  179.         moveNum   = ((move + startColor) / 2 + 1);
  180.         if ((needCR) && (!moveColor)) {
  181.             i = (condensed) ? 32 : 13;
  182.             (*txtHndl)[txtIndx++] = i;
  183.             needCR = false;
  184.         }
  185.         if ((!moveColor) || (move == gameIndex)) {
  186.             pcpydec(txt, i = (move / 2 + 1));
  187.             pcat(txt, "\p)  ");
  188.             if ((condensed) || (i > 9)) txt[0]--;
  189.             BlockMove(txt + 1, *txtHndl + txtIndx, txt[0]);
  190.             txtIndx += txt[0];
  191.             needCR = true;
  192.         }
  193.         if ((moveColor) && (move == gameIndex)) {
  194.             if (condensed) {
  195.                 BlockMove("...,", *txtHndl + txtIndx, 4);
  196.                 txtIndx += 4;
  197.             }
  198.             else {
  199.                 BlockMove("...      ", *txtHndl + txtIndx, 8);
  200.                 txtIndx += 8;
  201.             }
  202.             needCR = true;
  203.         }
  204.         RepositionBoard(frHndl, move, false);
  205.         Algebraic(frHndl, move, 0, txt);
  206.         if (move < numGameMoves - 1) {
  207.             if (condensed) {
  208.                 if (!moveColor) txt[++(txt[0])] = ',';
  209.             }
  210.             else {
  211.                 if (!moveColor) pcat(txt, "\p        ");
  212.                 if (txt[0] > 8) txt[0] = 8;
  213.             }
  214.         }
  215.         BlockMove(txt + 1, *txtHndl + txtIndx, txt[0]);
  216.         txtIndx += txt[0];
  217.         needCR = true;
  218.     }
  219.  
  220.     LockHandleHigh(txtHndl);
  221.     oldPort = SetFilePort(frHndl);
  222.  
  223.     CTENewUndo(CTEViewFromTE(teHndl), true);
  224.     TEDelete(teHndl);
  225.     selStart = (*teHndl)->selStart;
  226.     TEInsert(*txtHndl, txtIndx, teHndl);
  227.     selEnd = (*teHndl)->selStart;
  228.     TESetSelect(selStart, selEnd, teHndl);
  229.     TESelView(teHndl);
  230.     CTEAdjustTEBottom(teHndl);
  231.     CTEAdjustScrollValues(teHndl);
  232.     (*frHndl)->fileState.docDirty = true;
  233.  
  234.     SetPort(oldPort);
  235.     DisposeHandle(txtHndl);
  236.  
  237.     RepositionBoard(frHndl, gameIndex, false);
  238. }
  239.  
  240.  
  241.  
  242. /*****************************************************************************/
  243.  
  244.  
  245.  
  246. #pragma segment Window
  247. void    MovesFromText(FileRecHndl frHndl, Handle txtHndl, short txtLen,
  248.                       short beg, short end, Boolean slideMoves)
  249. {
  250.     WindowPtr        oldPort;
  251.     short            gameIndex, txtIndx, commentCount;
  252.     short            numUsed, numLglMoves, lastChar, c0, c1, i;
  253.     short            from, to, fromRow, fromCol, promoteTo, piece, f, t, r, c;
  254.     long            tick;
  255.     char            *cptr;
  256.     MoveListHndl    lglMoves;
  257.     static char        goodFirstFromChar[] = "Oo01abcdefghPNBRQK";
  258.  
  259.     oldPort  = SetFilePort(frHndl);
  260.     if (end == beg) end = txtLen;
  261.  
  262.     GenerateLegalMoves(frHndl);
  263.     gameIndex = (*frHndl)->doc.gameIndex;
  264.     txtIndx = beg;
  265.     commentCount = 0;
  266.  
  267.     tick = TickCount();
  268.  
  269.     for (;; txtIndx += numUsed) {
  270.  
  271.         if (!commentCount) {
  272.             numLglMoves = (*frHndl)->doc.numLegalMoves;
  273.             lglMoves    = (*frHndl)->doc.legalMoves;
  274.             if (!numLglMoves) break;
  275.                 /* Can't accept any moves, as there are no legal moves. */
  276.         }
  277.  
  278.         lastChar = end - txtIndx - 1;
  279.         if (lastChar < 1) break;
  280.             /* An algebraic move is at least 2 characters. */
  281.  
  282.         cptr = *txtHndl + txtIndx;
  283.         c0 = cptr[0];
  284.         numUsed = 1;        /* Always use at least 1 character per pass. */
  285.  
  286.         if (c0 == '[') {
  287.             ++commentCount;
  288.             continue;
  289.         }
  290.  
  291.         if (c0 == ']') {
  292.             if (commentCount) --commentCount;
  293.             continue;
  294.         }
  295.  
  296.         if (commentCount) continue;
  297.  
  298.         for (i = 0; goodFirstFromChar[i]; ++i)
  299.             if (c0 == goodFirstFromChar[i]) break;
  300.         if (!goodFirstFromChar[i]) continue;
  301.             /* Not good first character, so check next char. */
  302.  
  303.         fromRow = fromCol = -1;
  304.         from = to = 0;
  305.             /* From may be square or piece. */
  306.             /* To will be square. */
  307.             /* If to stays 0, then to = from, and from = PAWN.  More on this later. */
  308.  
  309.         for (;;) {        /* Used to break from for jump purposes. */
  310.  
  311.             if (i < 3) {        /* First character is castling character... */
  312.                 if (lastChar < 2) break;
  313.                     /* Not enough characters for kside castling. */
  314.                 if (cptr[numUsed] != '-') break;        /* Isn't a castle. */
  315.                 if (cptr[numUsed + 1] != '1') {            /* Isn't a black-wins notation. */
  316.                     if (cptr[++numUsed] != c0) break;    /* Isn't a castle. */
  317.                     from = (WhosMove(frHndl)) ? 25 : 95;
  318.                     to   = from + 2;
  319.                     if (lastChar < 4) {
  320.                         ++numUsed;
  321.                         break;
  322.                     }        /* Not enough characters for qside castling, so try kside. */
  323.                     if (cptr[++numUsed] != '-') break;        /* Isn't a castle. */
  324.                     if (cptr[++numUsed] != c0) break;        /* Isn't a castle. */
  325.                     to -= 4;
  326.                     ++numUsed;
  327.                     break;        /* Try qside castle. */
  328.                 }
  329.             }
  330.  
  331.             i -= 2;
  332.             if (i < 2) {    /* May be end-of-game notation. (1-0, 1/2, 0-1) */
  333.                 if (lastChar < 2) break;
  334.                     /* Not enough characters for end-of-game notation. */
  335.                 c1 = cptr[numUsed];
  336.                 if (c1 == '-') {
  337.                     if (cptr[numUsed + 1] == ('1' - i)) to = kWhiteResigns + i;
  338.                 }
  339.                 else if (c1 == '/') {
  340.                     if (cptr[numUsed + 1] == '2') to = kDrawGame;
  341.                 }
  342.                 if (to) EndTheGame(frHndl, to);
  343.                 to = 0;
  344.                 break;        /* Done processing this character, either way. */
  345.             }
  346.  
  347.             i -= 2;
  348.             if (i < 8) {        /* It is probably a pawn move.  The form is one of */
  349.                                 /* the following: e4, dxe4, d3xe4, d3e4, d3-e4.    */
  350.                                 /* Due to this, it can't be decided yet as to      */
  351.                                 /* whether we are looking at the from or the to    */
  352.                                 /* location.                                       */
  353.  
  354.                 from = PAWN;
  355.                 fromCol = i;    /* Might end up the toCol, so keep this in mind. */
  356.                 c1 = cptr[numUsed];
  357.                 if ((c1 >= '1') && (c1 <= '8')) {
  358.                     fromRow = 7 - (c1 - '1');    /* Might end up the toRow. */
  359.                     if (++numUsed > lastChar) break;
  360.                     c1 = cptr[numUsed];
  361.                 }
  362.                 if ((c1 == 'x') || (c1 == 'X') || (c1 == '-')) {
  363.                     if (++numUsed > lastChar) break;
  364.                     c0 = cptr[numUsed];
  365.                     if ((c0 < 'a') || (c0 > 'h')) break;
  366.                     if (++numUsed > lastChar) break;
  367.                     c1 = cptr[numUsed];
  368.                     if ((c1 >= '1') && (c1 <= '8')) {
  369.                         to = 10 * (7 - (c1 - '1')) + (c0 - 'a') + START_IBNDS;
  370.                         from = PAWN;        /* Assume it is a pawn move. */
  371.                         if ((fromRow != -1) && (fromCol != -1))
  372.                             from = 10 * fromRow + fromCol + START_IBNDS;
  373.                                 /* Move is of form d3-e4, so it doesn't have to be a pawn. */
  374.                         ++numUsed;
  375.                         break;
  376.                     }
  377.                 }
  378.                 else {        
  379.                     if (fromRow != -1) {            /* Of the form e4. */
  380.                         if ((c1 < 'a') || (c1 > 'h')) {
  381.                             from = PAWN;
  382.                             to = 10 * fromRow + fromCol + START_IBNDS;
  383.                             fromRow = fromCol = -1;        /* No hint of where from. */
  384.                         }
  385.                         else {
  386.                             if (++numUsed > lastChar) break;
  387.                             c0 = c1;
  388.                             c1 = cptr[numUsed];
  389.                             if ((c1 >= '1') && (c1 <= '8')) {
  390.                                 to = 10 * (7 - (c1 - '1')) + (c0 - 'a') + START_IBNDS;
  391.                                 from = PAWN;        /* Assume it is a pawn move. */
  392.                                 if ((fromRow != -1) && (fromCol != -1))
  393.                                     from = 10 * fromRow + fromCol + START_IBNDS;
  394.                                         /* Move is of form d3e4, so it doesn't have to be a pawn. */
  395.                                 ++numUsed;
  396.                                 break;
  397.                             }
  398.                         }
  399.                     }
  400.                     break;
  401.                 }
  402.             }
  403.  
  404.             else from = i - 7;        /* Remember which kind of piece. */
  405.  
  406.             c0 = cptr[numUsed];
  407.  
  408.             if (c0 == '(') {    /* Parens are optional -- just skip them. */
  409.                 if (++numUsed > lastChar) break;
  410.                 c0 = cptr[numUsed];
  411.             }
  412.  
  413.             if ((c0 >= 'a') && (c0 <= 'h')) {
  414.                 fromCol = c0 - 'a';
  415.                 if (++numUsed > lastChar) break;
  416.                 c0 = cptr[numUsed];
  417.             }
  418.             if ((c0 >= 'a') && (c0 <= 'h')) {
  419.                 if (++numUsed > lastChar) break;
  420.                 c1 = cptr[numUsed];
  421.                 if ((c1 >= '1') && (c1 <= '8')) {
  422.                     to = 10 * (7 - (c1 - '1')) + (c0 - 'a') + START_IBNDS;
  423.                     ++numUsed;
  424.                 }
  425.                 break;
  426.             }
  427.             if ((c0 >= '1') && (c0 <= '8')) {
  428.                 fromRow = 7 - (c0 - '1');
  429.                 if (++numUsed > lastChar) break;
  430.                 c0 = cptr[numUsed];
  431.             }
  432.  
  433.             if (c0 == ')') {    /* Parens are optional -- just skip them. */
  434.                 if (++numUsed > lastChar) break;
  435.                 c0 = cptr[numUsed];
  436.             }
  437.  
  438.             if ((c0 == 'x') || (c0 == 'X') || (c0 == '-')) {
  439.                 if (++numUsed > lastChar) break;
  440.                 c0 = cptr[numUsed];
  441.             }
  442.  
  443.             if ((c0 >= 'a') && (c0 <= 'h')) {
  444.                 if (++numUsed > lastChar) break;
  445.                 c1 = cptr[numUsed];
  446.                 if ((c1 >= '1') && (c1 <= '8')) {
  447.                     to = 10 * (7 - (c1 - '1')) + (c0 - 'a') + START_IBNDS;
  448.                     ++numUsed;
  449.                 }
  450.                 break;
  451.             }
  452.             else {
  453.                 if ((fromRow != -1) && (fromCol != -1)) {
  454.                     to = 10 * fromRow + fromCol + START_IBNDS;
  455.                     fromRow = fromCol = -1;        /* No hint of where from. */
  456.                 }
  457.                 break;
  458.             }
  459.  
  460.             break;
  461.         }
  462.  
  463.         if (!to) {
  464.             if ((fromRow != -1) && (fromCol != -1)) {
  465.                 to = 10 * fromRow + fromCol + START_IBNDS;
  466.                 fromRow = fromCol = -1;
  467.             }
  468.         }
  469.  
  470.         promoteTo = QUEEN;
  471.         if (to) {
  472.             if (numUsed < lastChar) {
  473.                 if (cptr[numUsed++] == '=') {
  474.                     if (numUsed < lastChar) {
  475.                         c1 = cptr[numUsed++];
  476.                         if (c1 == 'N') promoteTo = KNIGHT;
  477.                         if (c1 == 'B') promoteTo = BISHOP;
  478.                         if (c1 == 'R') promoteTo = ROOK;
  479.                     }
  480.                 }
  481.             }
  482.         }
  483.  
  484.         i = numLglMoves;
  485.         for (i = 0; i < numLglMoves; ++i) {
  486.             f = (**lglMoves)[i].moveFrom;
  487.             t = (**lglMoves)[i].moveTo;
  488.             if (to != t) continue;
  489.             if (from >= START_IBNDS) {
  490.                 if (from == f) break;
  491.                 continue;
  492.             }
  493.             piece = (*frHndl)->doc.theBoard[f];
  494.             if (piece < 0) piece = -piece;
  495.             if (piece == from) {
  496.                 r = (f - START_IBNDS) / 10;
  497.                 c = f - START_IBNDS - 10 * r;
  498.                 if (fromRow == -1) r = -1;
  499.                 if (fromCol == -1) c = -1;
  500.                 if ((fromRow == r) && (fromCol == c)) {
  501.                     from = f;
  502.                     break;
  503.                 }
  504.             }
  505.         }
  506.         if (i < numLglMoves) {
  507.             if (slideMoves) SlideThePiece(frHndl, from, to);
  508.             MakeMove(frHndl, from, to, promoteTo);
  509.             GenerateLegalMoves(frHndl);
  510.             ImageDocument(frHndl, true);
  511.             AdjustGameSlider(frHndl);
  512.             UpdateGameStatus(frHndl);
  513.             if (tick + 30 < TickCount()) {        /* Send max 1 AppleEvent per 1/2 sec. */
  514.                 tick = TickCount();
  515.                 if ((*frHndl)->doc.twoPlayer) SendGame(frHndl, kScrolling, nil);
  516.             }
  517.         }
  518.         else {
  519.             if ((!from) && (!to)) {
  520.                 if (numUsed > 1) --numUsed;
  521.                 for (;;) {
  522.                     c0 = cptr[numUsed++];
  523.                     if (numUsed >= lastChar) break;
  524.                     if ((c0 >= 'A') && (c0 <= 'Z')) continue;
  525.                     if ((c0 >= 'a') && (c0 <= 'z')) continue;
  526.                     if ((c0 >= '0') && (c0 <= '9')) continue;
  527.                     break;
  528.                 }
  529.             }
  530.         }
  531.     }
  532.  
  533.     if ((*frHndl)->doc.twoPlayer) SendGame(frHndl, kResync, nil);
  534.  
  535. }
  536.  
  537.  
  538.  
  539.  
  540. /*****************************************************************************/
  541.  
  542.  
  543.  
  544. #pragma segment Window
  545. void    MakeVerbose(StringPtr pstr)
  546. {
  547.     Str63                result;
  548.     Str32                piece, from, verb, to, epi;
  549.     short                i;
  550.     static StringPtr    cols[] = {"\pA,", "\pbee,", "\psea,", "\pdee,",
  551.                               "\pe,", "\pef,", "\pgee,", "\pH,"};
  552.  
  553.     if (!pstr[0]) return;
  554.  
  555.     result[0] = from[0] = to[0] = epi[0] = 0;
  556.  
  557.     pcpy(piece, "\ppawn ");
  558.     pcpy(verb,  "\ptoo, ");
  559.  
  560.     if (pstr[i = 1] < 'Z') {
  561.         if (pstr[i] == 'O') {
  562.             ++i;
  563.             if (pstr[0] < 3) return;
  564.             pcpy(result, "\pcastle ");
  565.             if (pstr[0] < 4) {
  566.                 pcat(result, "\p king side");
  567.                 pcpy(pstr, result);
  568.                 return;
  569.             }
  570.             if (pstr[4] == '+') {
  571.                 pcat(result, "\p king side, check");
  572.                 pcpy(pstr, result);
  573.                 return;
  574.             }
  575.             pcat(result, "\p kweenn side");
  576.             if (pstr[0] >= 6) {
  577.                 if (pstr[6] == '+') pcat(result, "\p, check");
  578.                 pcpy(pstr, result);
  579.             }
  580.             pcpy(pstr, result);
  581.             return;
  582.         }
  583.  
  584.         if (pstr[i] == 'N') pcpy(piece, "\pknight ");
  585.         if (pstr[i] == 'B') pcpy(piece, "\pbishop ");
  586.         if (pstr[i] == 'R') pcpy(piece, "\prook ");
  587.         if (pstr[i] == 'Q') pcpy(piece, "\pkweenn ");
  588.         if (pstr[i] == 'K') pcpy(piece, "\pking ");
  589.         ++i;
  590.     }
  591.  
  592.     if (i > pstr[0]) return;
  593.  
  594.     if ((pstr[i] >= '1') && (pstr[i] <= '8')) {
  595.         if (pstr[0] > (i + 1)) {
  596.             pcpy(from, "\pat, ");
  597.             pcatchr(from, pstr[i++], 1);
  598.             pcat(from, "\p, ");
  599.         }
  600.     }
  601.  
  602.     if ((pstr[i] >= 'a') && (pstr[i] <= 'h')) {
  603.         if (pstr[0] > (i + 1)) {
  604.             if ((pstr[i + 1] < '1') || (pstr[i + 1] > '8')) {
  605.                 pcpy(from, "\pat, ");
  606.                 pcat(from, cols[pstr[i++] - 'a']);
  607.                 pcatchr(from, ' ', 1);
  608.             }
  609.         }
  610.     }
  611.  
  612.     if (i > pstr[0]) return;
  613.  
  614.     if (pstr[i] == 'x') {
  615.         pcpy(verb, "\ptakes, ");
  616.         ++i;
  617.     }
  618.  
  619.     if (i > pstr[0]) return;
  620.  
  621.     if ((pstr[i] >= 'a') && (pstr[i] <= 'h')) {
  622.         pcat(to, cols[pstr[i++] - 'a']);
  623.         pcatchr(to, ' ', 1);
  624.     }
  625.  
  626.     if (i > pstr[0]) return;
  627.  
  628.     if ((pstr[i] >= '1') && (pstr[i] <= '8')) {
  629.         pcatchr(to, pstr[i++], 1);
  630.         pcatchr(to, ' ', 1);
  631.     }
  632.  
  633.     if (i <= pstr[0]) {
  634.         if (pstr[i] == '=') {
  635.             pcat(epi, "\p, promote too ");
  636.             if (++i > pstr[0]) return;
  637.             if (pstr[i] == 'N') pcat(epi, "\pknight ");
  638.             if (pstr[i] == 'B') pcat(epi, "\pbishop ");
  639.             if (pstr[i] == 'R') pcat(epi, "\prook ");
  640.             if (pstr[i] == 'Q') pcat(epi, "\pkweenn ");
  641.             ++i;
  642.         }
  643.     }
  644.  
  645.     if (i <= pstr[0]) {
  646.         if (pstr[i] == '+')
  647.             pcat(epi, "\p, check");
  648.     }
  649.  
  650.     pcpy(pstr, piece);
  651.     pcat(pstr, from);
  652.     pcat(pstr, verb);
  653.     pcat(pstr, to);
  654.     pcat(pstr, epi);
  655. }
  656.  
  657.  
  658.  
  659. /*****************************************************************************/
  660.  
  661.  
  662.  
  663. #pragma segment Window
  664. void    SayTheMove(FileRecHndl frHndl)
  665. {
  666.     Handle    txt;
  667.     Str255    pstr;
  668.     short    i;
  669.     Boolean    move;
  670.  
  671.     if (!(*frHndl)->doc.doSpeech) return;
  672.  
  673.     MakeMove(frHndl, -1, 0, 0);
  674.     i = (*frHndl)->doc.gameIndex;
  675.     move = Algebraic(frHndl, i, i + 1, pstr);
  676.     MakeMove(frHndl, 1, 0, 0);
  677.     if (move) MakeVerbose(pstr);
  678.  
  679.     switch (i = GameStatus(frHndl)) {
  680.         case kYouWin:
  681.         case kYouLose:
  682.             pcat(pstr, "\p mate");
  683.             break;
  684.     }
  685.  
  686.     txt = NewHandle(pstr[0]);
  687.     BlockMove(pstr + 1, *txt, pstr[0]);
  688.     SayText(nil, txt, (*frHndl)->doc.theVoice);
  689.     DisposeHandle(txt);
  690. }
  691.  
  692.  
  693.  
  694.